# renderer.py

r'''General purpose renderer.
'''

import functools
import itertools

from Tkinter import *
import ttk

class base(object):
    def __init__(self, base_frame, row_offset = 0, col_offset = 0):
        self.frame = base_frame
        self.row_offset = row_offset
        self.col_offset = col_offset

    def render_widgets(self, obj, widgets):
        info = tuple(widget.render(obj, self.frame, *self.inc(widget_num))
                     for widget_num, widget in enumerate(widgets))
        return (functools.partial(doall, tuple(i[0] for i in info)),
                any(i[1] for i in info),
                any(i[2] for i in info))

class render_row(base):
    def inc(self, widget_num):
        return self.row_offset + widget_num, self.col_offset

class render_col(base):
    def inc(self, widget_num):
        return self.row_offset, self.col_offset + widget_num

class obj_renderer(render_col):
    def __init__(self, base_frame, row_offset = 0, col_offset = 0):
        render_col.__init__(self, base_frame, row_offset, col_offset)
        self.frame.columnconfigure(col_offset, minsize=40, weight=1)

    def render(self, input_fields, method_interfaces=(), obj=None,
               button_text="Update", command=None):
        row_offset = 0
        fns = []
        resizeable_width = resizeable_height = False
        if method_interfaces:
            fn, resizeable_width_flag, resizeable_height_flag = \
              self.render_methods(obj, method_interfaces, row_offset)
            fns.append(fn)
            resizeable_width = resizeable_width or resizeable_width_flag
            resizeable_height = resizeable_height or resizeable_height_flag
            row_offset += 1
        if input_fields:
            fn, resizeable_width_flag, resizeable_height_flag = \
              self.render_inputs(obj, input_fields, row_offset)
            fns.append(fn)
            resizeable_width = resizeable_width or resizeable_width_flag
            resizeable_height = resizeable_height or resizeable_height_flag
            row_offset += 1
        button_frame = ttk.Frame(self.frame)
        button_frame.grid(row=self.row_offset + row_offset, column=0)
        if command is None:
            command_fn = functools.partial(catall, fns)
        else:
            command_fn = lambda: command(catall(fns))
        ttk.Button(button_frame, text=button_text, command=command_fn) \
          .grid(row=0, column=0)
        ttk.Button(button_frame, text="Cancel", command=self.frame.destroy) \
          .grid(row=0, column=1)
        return resizeable_width, resizeable_height

    def render_obj(self, obj):
        klass = type(obj)
        method_interfaces = klass.methods
        input_fields = klass.input_fields
        return self.render(input_fields, method_interfaces, obj)

    def render_method(self, input_fields, button_text, command):
        return self.render(input_fields, button_text=button_text,
                           command=command)

    def render_methods(self, obj, method_interfaces, row_offset):
        #print "render_methods", len(method_interfaces)
        method_frame = ttk.Frame(self.frame)
        method_frame.grid(row=self.row_offset + row_offset, column=0)
        return render_col(method_frame).render_widgets(obj, method_interfaces)

    def render_inputs(self, obj, input_fields, row_offset):
        #print "render_inputs", len(input_fields)
        input_frame = ttk.Frame(self.frame)
        input_frame.grid(row=self.row_offset + row_offset, column=0,
                         sticky=N+S+W+E)
        input_frame.columnconfigure(1, minsize=40, weight=1)
        ans = fn, resizeable_width, resizeable_height = \
          render_row(input_frame).render_widgets(obj, input_fields)
        if resizeable_height:
            self.frame.rowconfigure(self.row_offset + row_offset, weight = 1)
        return ans

def doall(fns):
    ans = tuple(fn() for fn in fns)
    #print "doall:", ans
    return ans

def catall(fns):
    ans = tuple(itertools.chain.from_iterable(fn() for fn in fns))
    #print "catall:", ans
    return ans
